package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.CreateActivityParam;
import com.example.lotterysystem.controller.param.CreatePrizeByActivityParam;
import com.example.lotterysystem.controller.param.CreateUserByActivityParam;
import com.example.lotterysystem.controller.param.PageParam;
import com.example.lotterysystem.dao.dataobject.ActivityDO;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.dao.dataobject.ActivityUserDO;
import com.example.lotterysystem.dao.dataobject.PrizeDO;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.IActivityService;
import com.example.lotterysystem.service.dto.ActivityDTO;
import com.example.lotterysystem.service.dto.ActivityDetailDTO;
import com.example.lotterysystem.service.dto.CreateActivityDTO;
import com.example.lotterysystem.service.dto.PageListDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Slf4j
@Service
public class ActivityServiceImpl implements IActivityService {
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private RedisUtil redisUtil;
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;

    @Override
    public CreateActivityDTO createActivityDTO(CreateActivityParam param) {
        //校验活动信息是否正确
        checkActivityInfo(param);
        //保存活动信息
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);


        //保存活动关联的奖品信息
        List<CreatePrizeByActivityParam> prizeByActivityParamList = param.getPrizeByActivityParamList();
        List<ActivityPrizeDO> activityPrizeDOList = prizeByActivityParamList.stream()
                .map(prizeParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);
        //保存活动关联的人员信息
        List<CreateUserByActivityParam> userByActivityParamList = param.getUserByActivityParamList();
        List<ActivityUserDO> activityUserDOList = userByActivityParamList.stream()
                .map(userParam -> {
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);
        //整合完整的活动信息, 存放在redis中
        //activityID : ActivityDetailDTO
        //获取次要查询的奖品id
        List<Long> prizeIds = param.getPrizeByActivityParamList().stream().map(CreatePrizeByActivityParam::getPrizeId)
                .distinct().collect(Collectors.toList());
        //获取奖品的基本属性
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(activityDO, prizeDOList, activityPrizeDOList, activityUserDOList);
        cacheActivity(detailDTO);

        //构造返回
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        //获取总量
        int total = activityMapper.count();
        //获取当前页列表
        List<ActivityDO> activityDOS = activityMapper.selectActivityList(param.offset(), param.getPageSize());
        List<ActivityDTO> activityDTOS = activityDOS.stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(total, activityDTOS);
    }

    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if(null == activityId){
            log.warn("查询活动信息失败: activityId为空");
            return null;
        }
        //查询redis
        ActivityDetailDTO detailDTO = getActivityFromCache(activityId);
        if(null != detailDTO){
            log.info("查询活动信息成功: detailDTO={}", detailDTO);
            return detailDTO;
        }
        //如果redis不存在, 则查表
        //活动表
        ActivityDO activityDO = activityMapper.selectById(activityId);
        //人员表
        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectByActivityId(activityId);
        //活动奖品表
        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectBuActivityId(activityId);
        //奖品表
        List<Long> prizeIds = activityPrizeDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        //整合, 存放在redis中
        detailDTO = convertToActivityDetailDTO(activityDO,prizeDOList,activityPrizeDOList, activityUserDOList);
        cacheActivity(detailDTO);
        //返回
        return detailDTO;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if(null == activityId){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_CACHE_IS_EMPTY);
        }

        //查询表数据: 活动表, 关联奖品, 关联人员, 奖品信息表
        //活动表
        ActivityDO activityDO = activityMapper.selectById(activityId);
        //人员表
        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectByActivityId(activityId);
        //活动奖品表
        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectBuActivityId(activityId);
        //奖品表
        List<Long> prizeIds = activityPrizeDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        //整合, 存放在redis中
        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(activityDO,prizeDOList,activityPrizeDOList, activityUserDOList);
        cacheActivity(detailDTO);

    }

    private void cacheActivity(ActivityDetailDTO detailDTO) {
        //ACTVITY_activityId : ActivityDetailDTO(json)
        if(null == detailDTO || null == detailDTO.getActivityId()){
            log.warn("要缓存的活动信息不存在");
            return;
        }
        try{
            redisUtil.set(ACTIVITY_PREFIX + detailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detailDTO),
                    ACTIVITY_TIMEOUT);

        }catch (Exception e){
            log.error("缓存活动异常: ActivityDetailDTO={}", JacksonUtil.writeValueAsString(detailDTO), e);
        }


    }

    private ActivityDetailDTO getActivityFromCache(Long activityId){
        if(null == activityId){
            log.warn("获取到缓存活动数据的activityId为空");
            return null;
        }
        try{
            String str = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if(!StringUtils.hasText(str)){
                log.warn("获取到缓存活动数据为空, key={}", ACTIVITY_PREFIX + activityId);
                return null;
            }
            ActivityDetailDTO detailDTO = JacksonUtil.readValue(str, ActivityDetailDTO.class);
            return detailDTO;
        }catch (Exception e){
            log.error("从缓存中获取活动信息异常, key={}", ACTIVITY_PREFIX + activityId, e);
            return null;
        }
    }

    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<PrizeDO> prizeDOList,
                                                         List<ActivityPrizeDO> activityPrizeDOList,
                                                         List<ActivityUserDO> activityUserDOList) {
        ActivityDetailDTO activityDetailDTO = new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));

        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList.stream().map(apDO -> {
            ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
            prizeDTO.setId(apDO.getPrizeId());
            //apDO: {prizeId, amount, status}, {prizeId, amount, status}
            //prizeDO: {id, name, ...}, {id, name, ...}, {id, name, ...}
            Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream().filter(prizeDO -> {
                return prizeDO.getId().equals(apDO.getPrizeId());
            }).findFirst();
            //如果PrizeDO为空, 不执行下面的方法
            optionalPrizeDO.ifPresent(prizeDO -> {
                prizeDTO.setName(prizeDO.getName());
                prizeDTO.setImageUrl(prizeDO.getImageUrl());
                prizeDTO.setPrice(prizeDO.getPrice());
                prizeDTO.setDescription(prizeDO.getDescription());
            });

            prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
            prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
            prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));

            return prizeDTO;
        }).collect(Collectors.toList());
        activityDetailDTO.setPrizeDTOList(prizeDTOList);

        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList.stream().map(auDO -> {
            ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
            userDTO.setUserId(auDO.getUserId());
            userDTO.setUserName(auDO.getUserName());
            userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));

            return userDTO;
        }).collect(Collectors.toList());

        activityDetailDTO.setUserDTOList(userDTOList);

        return activityDetailDTO;


    }

    private void checkActivityInfo(CreateActivityParam param) {
        if (null == param) throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_IS_EMPTY);
        //人员id在人员表中存在
        List<Long> userIds = param.getUserByActivityParamList().stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct() //去重
                .collect(Collectors.toList());
        List<Long> existIds = userMapper.selectExistByIds(userIds);
        if(CollectionUtils.isEmpty(existIds)){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id -> {
            if (!existIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });
        //奖品id在奖品表中存在
        List<Long> prizeIds = param.getPrizeByActivityParamList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct() //去重
                .collect(Collectors.toList());
        List<Long> existPrizeIds = prizeMapper.selectExistByIds(prizeIds);
        if(CollectionUtils.isEmpty(prizeIds)){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id -> {
            if (!existPrizeIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        //设置的人员数量 >= 奖品数量

        int userAmount = param.getUserByActivityParamList().size();
        long prizeAmount = param.getPrizeByActivityParamList()
                .stream().mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if (userAmount < prizeAmount) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_AMOUNT_ERROR);
        }

        //校验活动奖品等级有效性
        param.getPrizeByActivityParamList().forEach(prize -> {
            if (null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRICE_TIERS_ERROR);
            }
        });
    }
}
